package com.minelx.scriptbuilder.script;

import com.minelx.scriptbuilder.script.attribute.value.Attribute;
import com.minelx.scriptbuilder.script.attribute.value.IAttribute;

import java.util.List;
import java.util.Stack;

public class VariableManager {

	private final Stack<FunctionFrame> frames;

	private final IAttribute globals;

	public VariableManager() {
		frames = new Stack<>();
		globals = new Attribute();
	}

	public void global(String name, IAttribute value) {
		globals.set(name, value);
	}

	public IVariable global(String name) {
		return globals.get(name);
	}

	public IVariable temporary(String name) {
		throwIfNoFrame();
		return currentFrame().param(name);
	}

	public void variable(String name, IAttribute value) {
		writeVariable(name).assigned(value);
	}

	public IVariable variable(String name) {
		return new Var(name);
	}

	public void pushFrame(List<String> names, List<IAttribute> values) {
		frames.push(new FunctionFrame(names, values));
	}

	public FunctionFrame currentFrame() {
		throwIfNoFrame();
		return frames.peek();
	}

	public FunctionFrame popFrame() {
		return frames.pop();
	}

	private boolean noFrame() {
		return frames.isEmpty();
	}

	public IVariable readVariable(String name) {
		if (noFrame()) {
			return global(name);
		}
		// FIXME 2021/3/26  wait for me!!!    FunctionFrame.hasParam
		IVariable temporary = temporary(name);
		if (temporary.exists()) {
			return temporary;
		}
		return global(name);
	}

	private IVariable writeVariable(String name) {
		if (noFrame()) {
			return global(name);
		}
		return temporary(name);
	}

	private void throwIfNoFrame() {
		if (noFrame()) {
			throw new IllegalStateException("there is no frame for now.");
		}
	}

	private class Var implements IVariable {
		private final String name;

		public Var(String name) {
			this.name = name;
		}

		@Override
		public IAttribute value() {
			return readVariable(name).value();
		}

		@Override
		public void assigned(IAttribute as) {
			writeVariable(name).assigned(as);
		}
	}
}
